﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Reflection;
using System.Data.Objects;
using System.Linq.Expressions;

namespace Exhibition.Common.Helper
{
    public static class EntityHelper
    {
        #region DataTable转换成实体

        /// <summary>
        /// 从DataTable里读取数据将数据转换成实例列表
        /// </summary>
        /// <typeparam name="T">实例类型</typeparam>
        /// <param name="reader">DataReader</param>
        /// <returns>列表</returns>
        public static IList<T> GetListFromReader<T>(IDataReader reader)
        {
            IList<T> list = new List<T>();
            Type type = typeof(T);
            PropertyInfo[] props = type.GetProperties();
            FieldInfo[] fields = type.GetFields();
            DataTable table = null;
            List<string> namesInSelect = new List<string>();

            if (table == null)
            {
                table = reader.GetSchemaTable();
                foreach (DataRow row in table.Rows)
                {
                    namesInSelect.Add(row[0].ToString().ToLower());
                }
            }
            T instance = Activator.CreateInstance<T>();
            foreach (PropertyInfo property in props)
            {
                if (property.PropertyType.FullName.StartsWith("System.Collections.Generic.IList`1"))
                {
                    continue;
                }
                if (!namesInSelect.Contains(property.Name.ToLower()))
                {
                    continue;
                }
                Object value = reader[property.Name];
                Type propertyType = property.PropertyType;
                if (propertyType.IsEnum)
                {
                    if (Convert.IsDBNull(value))
                    {
                        property.SetValue(instance, null, null);
                    }
                    else
                    {
                        object o = Enum.Parse(propertyType, value.ToString());
                        property.SetValue(instance, o, null);
                    }
                    continue;
                }
                switch (propertyType.FullName)
                {
                    case "System.String":
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, string.Empty, null);
                        }
                        else
                        {
                            property.SetValue(instance, value.ToString(), null);
                        }
                        break;
                    case "System.DateTime":
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, null, null);
                        }
                        else
                        {
                            DateTime dt;
                            DateTime.TryParse(value.ToString(), out dt);
                            property.SetValue(instance, dt, null);
                        }
                        break;
                    case "System.Int16":
                    case "System.Int32":
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, 0, null);
                        }
                        else
                        {
                            int digital;
                            int.TryParse(value.ToString(), out digital);
                            property.SetValue(instance, digital, null);
                        }
                        break;
                    case "System.Decimal":
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, 0m, null);
                        }
                        else
                        {
                            decimal dc;
                            decimal.TryParse(value.ToString(), out dc);
                            property.SetValue(instance, dc, null);
                        }
                        break;
                    case "System.Boolean":
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, null, null);
                        }
                        else
                        {
                            bool bl;
                            bool.TryParse(value.ToString(), out bl);
                            property.SetValue(instance, bl, null);
                        }
                        break;
                    default:
                        break;
                }
            }

            foreach (FieldInfo property in fields)
            {
                if (property.FieldType.FullName.StartsWith("System.Collections.Generic.IList`1"))
                {
                    continue;
                }
                if (!namesInSelect.Contains(property.Name.ToLower()))
                {
                    continue;
                }
                Object value = reader[property.Name];
                Type propertyType = property.FieldType;
                if (propertyType.IsEnum)
                {
                    if (Convert.IsDBNull(value))
                    {
                        property.SetValue(instance, null);
                    }
                    else
                    {
                        object o = Enum.Parse(propertyType, value.ToString());
                        property.SetValue(instance, o);
                    }
                    continue;
                }
                switch (propertyType.FullName)
                {
                    case "System.String":
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, string.Empty);
                        }
                        else
                        {
                            property.SetValue(instance, value.ToString());
                        }
                        break;
                    case "System.DateTime":
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, null);
                        }
                        else
                        {
                            DateTime dt;
                            DateTime.TryParse(value.ToString(), out dt);
                            property.SetValue(instance, dt);
                        }
                        break;
                    case "System.Int16":
                    case "System.Int32":
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, 0);
                        }
                        else
                        {
                            int digital;
                            int.TryParse(value.ToString(), out digital);
                            property.SetValue(instance, digital);
                        }
                        break;
                    case "System.Decimal":
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, 0m);
                        }
                        else
                        {
                            decimal dc;
                            decimal.TryParse(value.ToString(), out dc);
                            property.SetValue(instance, dc);
                        }
                        break;
                    case "System.Boolean":
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, null);
                        }
                        else
                        {
                            bool bl;
                            bool.TryParse(value.ToString(), out bl);
                            property.SetValue(instance, bl);
                        }
                        break;
                    default:
                        break;
                }

            }

            list.Add(instance);

            return list;
        }

        /// <summary>
        /// 从DataReader里读取数据将数据转换成实例
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="reader">DataReader</param>
        /// <returns>实例</returns>
        public static T GetEntityFromReader<T>(IDataReader reader)
        {
            Type type = typeof(T);
            PropertyInfo[] props = type.GetProperties();
            FieldInfo[] fields = type.GetFields();
            T instance = Activator.CreateInstance<T>();
            DataTable table = null;
            List<string> namesInSelect = new List<string>();
            if (reader.Read())
            {
                if (table == null)
                {
                    table = reader.GetSchemaTable();
                    foreach (DataRow row in table.Rows)
                    {
                        namesInSelect.Add(row[0].ToString().ToLower());
                    }
                }
                foreach (PropertyInfo property in props)
                {
                    if (property.PropertyType.FullName.StartsWith("System.Collections.Generic.IList`1"))
                    {
                        continue;
                    }
                    if (!namesInSelect.Contains(property.Name.ToLower()))
                    {
                        continue;
                    }
                    Object value = reader[property.Name];
                    Type propertyType = property.PropertyType;
                    if (propertyType.IsEnum)
                    {
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, null, null);
                        }
                        else
                        {
                            object o = Enum.Parse(propertyType, value.ToString());
                            property.SetValue(instance, o, null);
                        }
                        continue;
                    }
                    switch (propertyType.FullName)
                    {
                        case "System.String":
                            if (Convert.IsDBNull(value))
                            {
                                property.SetValue(instance, string.Empty, null);
                            }
                            else
                            {
                                property.SetValue(instance, value, null);
                            }
                            break;
                        case "System.DateTime":
                            if (Convert.IsDBNull(value))
                            {
                                property.SetValue(instance, null, null);
                            }
                            else
                            {
                                DateTime dt;
                                DateTime.TryParse(value.ToString(), out dt);
                                property.SetValue(instance, dt, null);
                            }
                            break;
                        case "System.Int16":
                        case "System.Int32":
                            if (Convert.IsDBNull(value))
                            {
                                property.SetValue(instance, 0, null);
                            }
                            else
                            {
                                int digital;
                                int.TryParse(value.ToString(), out digital);
                                property.SetValue(instance, digital, null);
                            }
                            break;
                        case "System.Decimal":
                            if (Convert.IsDBNull(value))
                            {
                                property.SetValue(instance, 0m, null);
                            }
                            else
                            {
                                decimal dc;
                                decimal.TryParse(value.ToString(), out dc);
                                property.SetValue(instance, dc, null);
                            }
                            break;
                        case "System.Boolean":
                            if (Convert.IsDBNull(value))
                            {
                                property.SetValue(instance, null, null);
                            }
                            else
                            {
                                bool bl;
                                bool.TryParse(value.ToString(), out bl);
                                property.SetValue(instance, bl, null);
                            }
                            break;
                        default:
                            break;
                    }
                }
                foreach (FieldInfo property in fields)
                {
                    if (property.FieldType.FullName.StartsWith("System.Collections.Generic.IList`1"))
                    {
                        continue;
                    }
                    if (!namesInSelect.Contains(property.Name.ToLower()))
                    {
                        continue;
                    }
                    Object value = reader[property.Name];
                    Type propertyType = property.FieldType;
                    if (propertyType.IsEnum)
                    {
                        if (Convert.IsDBNull(value))
                        {
                            property.SetValue(instance, null);
                        }
                        else
                        {
                            object o = Enum.Parse(propertyType, value.ToString());
                            property.SetValue(instance, o);
                        }
                        continue;
                    }
                    switch (propertyType.FullName)
                    {
                        case "System.String":
                            if (Convert.IsDBNull(value))
                            {
                                property.SetValue(instance, string.Empty);
                            }
                            else
                            {
                                property.SetValue(instance, value);
                            }
                            break;
                        case "System.DateTime":
                            if (Convert.IsDBNull(value))
                            {
                                property.SetValue(instance, null);
                            }
                            else
                            {
                                DateTime dt;
                                DateTime.TryParse(value.ToString(), out dt);
                                property.SetValue(instance, dt);
                            }
                            break;
                        case "System.Int16":
                        case "System.Int32":
                            if (Convert.IsDBNull(value))
                            {
                                property.SetValue(instance, 0);
                            }
                            else
                            {
                                int digital;
                                int.TryParse(value.ToString(), out digital);
                                property.SetValue(instance, digital);
                            }
                            break;
                        case "System.Decimal":
                            if (Convert.IsDBNull(value))
                            {
                                property.SetValue(instance, 0m);
                            }
                            else
                            {
                                decimal dc;
                                decimal.TryParse(value.ToString(), out dc);
                                property.SetValue(instance, dc);
                            }
                            break;
                        case "System.Boolean":
                            if (Convert.IsDBNull(value))
                            {
                                property.SetValue(instance, null);
                            }
                            else
                            {
                                bool bl;
                                bool.TryParse(value.ToString(), out bl);
                                property.SetValue(instance, bl);
                            }
                            break;
                        default:
                            break;
                    }

                }
            }
            else
            {
                return default(T);
            }
            return instance;
        }





        /// <summary>
        /// 从DataTable里读取数据将数据转换成实例
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="reader">DataReader</param>
        /// <returns>实例</returns>
        public static T GetEntityFromDataTable<T>(DataTable dt)
        {
            Type type = typeof(T);

            object t = Activator.CreateInstance(type);


            string tempName = "";

            foreach (DataRow dr in dt.Rows)
            {


                // 获得此模型的公共属性
                PropertyInfo[] propertys = typeof(T).GetProperties();

                foreach (PropertyInfo pi in propertys)
                {
                    tempName = pi.Name;

                    // 检查DataTable是否包含此列
                    if (dt.Columns.Contains(tempName))
                    {
                        // 判断此属性是否有Setter
                        if (!pi.CanWrite) continue;
                        if (dr[tempName] != DBNull.Value)
                        {

                            if (pi.PropertyType.ToString() == "System.String" || pi.PropertyType.ToString() == "System.Nullable`1[System.String]")
                                if (dr[tempName] != null)
                                {
                                    pi.SetValue(t, Convert.ToString(dr[tempName]), null);
                                }
                                else
                                {
                                    pi.SetValue(t, null, null);
                                }

                            if (pi.PropertyType.ToString() == "System.Boolean" || pi.PropertyType.ToString() == "System.Nullable`1[System.Boolean]")
                                if (dr[tempName] != null)
                                {
                                    pi.SetValue(t, Convert.ToBoolean(dr[tempName]), null);
                                }
                                else
                                {
                                    pi.SetValue(t, null, null);
                                }

                            if (pi.PropertyType.ToString() == "System.Int32" || pi.PropertyType.ToString() == "System.Nullable`1[System.Int32]")
                                if (dr[tempName] != null)
                                {
                                    pi.SetValue(t, Convert.ToInt32(dr[tempName]), null);
                                }
                                else
                                {
                                    pi.SetValue(t, null, null);
                                }

                            if (pi.PropertyType.ToString() == "System.DateTime" || pi.PropertyType.ToString() == "System.Nullable`1[System.DateTime]")
                                if (dr[tempName] != null)
                                {
                                    pi.SetValue(t, Convert.ToDateTime(dr[tempName]), null);
                                }
                                else
                                {
                                    pi.SetValue(t, null, null);
                                }

                        }
                    }
                }


            }

            return (T)t;


        }
        /// <summary>
        /// 从DataReader里读取数据将数据转换成实例
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="reader">DataReader</param>
        /// <returns>实例</returns>
        public static T ToEntity<T>(this IDataReader reader)
        {
            return GetEntityFromReader<T>(reader);
        }


        /// <summary>
        /// 从DataTable里读取数据将数据转换成实例
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="reader">DataReader</param>
        /// <returns>实例</returns>
        public static T ToEntity<T>(this DataTable dt)
        {
            return GetEntityFromDataTable<T>(dt);
        }


        /// <summary>
        /// 从DataReader里读取数据将数据转换成实例列表
        /// </summary>
        /// <typeparam name="T">实例类型</typeparam>
        /// <param name="reader">DataReader</param>
        /// <returns>列表</returns>
        public static IList<T> ToEntityList<T>(this IDataReader reader)
        {
            return GetListFromReader<T>(reader);
        }

        #endregion

        #region 实体转化成SQL参数

        /// <summary>
        /// 实体转化成SQL参数
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static List<Parameter> ToParameter<T>(this T t)
        {
            return GetParameter<T>(t);
        }

        /// <summary>
        /// 实体转化成SQL参数
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static List<Parameter> GetParameter<T>(T t)
        {
            List<Parameter> parames = new List<Parameter>();
            Type type = typeof(T);

            //string tempName = "";

            // 获得此模型的公共属性
            PropertyInfo[] propertys = t.GetType().GetProperties();

            PropertyInfo[] fields = type.GetProperties();
            foreach (PropertyInfo fi in fields)
            {
                Parameter tempField = new Parameter();
                object obj = fi.GetValue(t, null);
                if (obj != null)
                {
                    tempField.DType = TypeToDbType(fi.PropertyType);
                    tempField.Value = obj.ToString();
                    tempField.ValuePrm = fi.Name;
                    parames.Add(tempField);
                }
            }
            return parames;
        }

        public static DbType TypeToDbType(Type t)
        {
            DbType dbt;
            try
            {
                dbt = (DbType)Enum.Parse(typeof(DbType), t.Name);
            }
            catch
            {
                dbt = DbType.Object;
            }
            return dbt;
        }
        #endregion
        /// <summary>
        /// 扩展sql In方法
        /// </summary>
        /// <typeparam name="T">表名</typeparam>
        /// <typeparam name="TValue">需要用IN字段类型 比如说ID in(....),则为ID的数据类型</typeparam>
        /// <param name="query">IQueryable 数据</param>
        /// <param name="valueSelector"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public static IQueryable<T> ExtWhereIn<T, TValue>(this ObjectQuery<T> query, Expression<Func<T, TValue>> valueSelector, IEnumerable<TValue> values)
        {
            return query.Where(BuildContainsExpression<T, TValue>(valueSelector, values));
        }

        private static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
        Expression<Func<TElement, TValue>> valueSelector,
        IEnumerable<TValue> values)
        {
            if (null == valueSelector)
            {
                throw new ArgumentNullException("valueSelector");
            }
            if (null == values)
            {
                throw new ArgumentNullException("values");
            }
            ParameterExpression p = valueSelector.Parameters.Single();
            //p => valueSelector(p) == values[0] || valueSelector(p) == ...            
            if (!values.Any())
            {
                return e => false;
            }
            var equals =
                values.Select(
                    value =>
                    (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
            var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
            return Expression.Lambda<Func<TElement, bool>>(body, p);
        }


        public static IQueryable<T> ExtWhereNotIn<T, TValue>(this ObjectQuery<T> query, Expression<Func<T, TValue>> valueSelector, IEnumerable<TValue> values)
        {
            return query.Where(NotContains<T, TValue>(valueSelector, values));
        }

        /// <summary>
        /// not contains 查询不包含
        /// </summary>
        /// <typeparam name="TElement"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="valueSelector"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        private static Expression<Func<TElement, bool>> NotContains<TElement, TValue>(
         Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
        {

            if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }

            if (null == values) { throw new ArgumentNullException("values"); }

            ParameterExpression p = valueSelector.Parameters.Single();

            // p => valueSelector(p) == values[0] || valueSelector(p) == ...

            if (!values.Any())
            {
                return e => true;
            }

            var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));

            var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
            body = Expression.Not(body);
            return Expression.Lambda<Func<TElement, bool>>(body, p);
        }


    }
}
